package com.ds.lens.manager.scheduler;

import com.ds.lens.data.common.util.HttpUtil;
import com.ds.lens.manager.bo.MuteTaskBO;
import com.ds.lens.manager.service.ScheduleMuteTask;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import javax.annotation.PreDestroy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * @author WeiShaoying
 * @date 2019/11/18 下午6:15
 */
@Configuration
public class MuteScheduleConfig implements SchedulingConfigurer {

    @Getter
    private volatile ScheduledTaskRegistrar scheduledTaskRegistrar;

    private static final String FORMAT = "0 mm HH dd MM ?";

    private static final Long OVERDUE = 5 * 60000L;

    @Value(value = "${mute.service.url}")
    private String muteServiceUrl;

    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, CronTask> cronTasks = new ConcurrentHashMap<>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        this.scheduledTaskRegistrar = scheduledTaskRegistrar;
    }

    public void refreshTasks(Map<Long, MuteTaskBO> muteTaskBOMap) {
        removeExistsTask(muteTaskBOMap);
        long systemTime = System.currentTimeMillis();
        muteTaskBOMap.forEach((aLong, muteTaskBO) -> {
            if (muteTaskBO.getState() == 1 &&
                    (muteTaskBO.getScheduleType() == 1 || compareDate(muteTaskBO.getStartTime(), new Date()))) {
                String startExpression = getExpression(muteTaskBO.getStartTime());
                muteTask(aLong, true, startExpression);
            }
            if ((muteTaskBO.getState() == 1) && (new Date().getTime() - muteTaskBO.getStartTime().getTime() > OVERDUE)) {
                request(muteTaskBO.getId(), true);
            }
            if (muteTaskBO.getEndTime() != null) {
                if (muteTaskBO.getState() == 2 && systemTime - muteTaskBO.getEndTime().getTime() > OVERDUE) {
                    request(muteTaskBO.getId(), false);
                } else {
                    muteTask(aLong, false, getExpression(muteTaskBO.getEndTime()));
                }
            }
        });
    }

    private void muteTask(Long id, Boolean mute, String expression) {
        if (!cronTasks.containsKey(id.toString() + mute.toString())) {
            addTask(id, mute, expression);
        } else if (!cronTasks.get(id.toString() + mute.toString()).getExpression().equals(expression)) {
            cancelTask(id, mute);
            addTask(id, mute, expression);
        }
    }


    private void removeExistsTask(Map<Long, MuteTaskBO> muteTaskBOMap) {
        Set<String> keys = scheduledFutures.keySet();
        keys.forEach(s -> {
            Long key = Long.valueOf(s.replace(Boolean.TRUE.toString(), "").replace(Boolean.FALSE.toString(), ""));
            if (muteTaskBOMap.get(key) == null) {
                scheduledFutures.get(s).cancel(false);
                scheduledFutures.remove(s);
                cronTasks.remove(s);
            }
        });
    }

    private void addTask(Long id, Boolean mute, String expression) {
        ScheduleMuteTask muteTask = new ScheduleMuteTask(id, mute, muteServiceUrl);
        CronTask task = new CronTask(muteTask, expression);
        ScheduledFuture<?> future = scheduledTaskRegistrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
        cronTasks.put(id.toString() + mute.toString(), task);
        scheduledFutures.put(id.toString() + mute.toString(), future);
    }

    private void cancelTask(Long id, Boolean mute) {
        scheduledFutures.get(id.toString() + mute.toString()).cancel(false);
        scheduledFutures.remove(id.toString() + mute.toString());
        cronTasks.remove(id.toString() + mute.toString());
    }

    private String getExpression(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
        return dateFormat.format(date);
    }

    private boolean compareDate(Date d1, Date d2) {
        return d1.getYear() >= d2.getYear() && d1.getMonth() >= d2.getMonth()
                && d1.getDay() >= d2.getDay() && d1.getHours() >= d2.getHours();
    }

    private void request(Long id, Boolean mute) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", id);
        params.put("mute", mute);
        HttpUtil.get(muteServiceUrl, params, null, 1000);
    }

    @PreDestroy
    public void destroy() {
        this.scheduledTaskRegistrar.destroy();
    }
}
